Fedezze fel a tulajdonság-alapú tesztelést a Python Hypothesis könyvtárával. Lépjen túl a példateszteken, hogy szélsőértékeket találjon és robusztusabb szoftvert építsen.
Az egységteszteken túl: Mélyreható betekintés a tulajdonság-alapú tesztelésbe a Python Hypothesis könyvtárával
A szoftverfejlesztés világában a tesztelés a minőség alapja. Évtizedek óta a domináns paradigma a példa-alapú tesztelés. Gondosan összeállítjuk a bemeneteket, meghatározzuk a várt kimeneteket, és állításokat írunk annak ellenőrzésére, hogy kódunk a tervek szerint működik-e. Ez a megközelítés, amely olyan keretrendszerekben található meg, mint a unittest
és a pytest
, hatékony és alapvető. De mi van, ha azt mondanám, hogy létezik egy kiegészítő megközelítés, amely olyan hibákat tárhat fel, amelyekre soha nem is gondoltál volna?
Üdvözöljük a tulajdonság-alapú tesztelés világában, egy olyan paradigmában, amely a fókuszát a specifikus példák teszteléséről a kód általános tulajdonságainak ellenőrzésére helyezi át. És a Python ökoszisztémában ennek a megközelítésnek a vitathatatlan bajnoka egy könyvtár, melynek neve Hypothesis.
Ez az átfogó útmutató elvezeti Önt a teljes kezdő szinttől a tulajdonság-alapú tesztelés magabiztos gyakorlójává a Hypothesis segítségével. Feltárjuk az alapvető koncepciókat, elmerülünk a gyakorlati példákban, és megtanuljuk, hogyan integrálhatja ezt a hatékony eszközt a mindennapi fejlesztési munkafolyamatába, hogy robusztusabb, megbízhatóbb és hibáknak ellenállóbb szoftvert építsen.
Mi az a tulajdonság-alapú tesztelés? Gondolkodásmódváltás
A Hypothesis megértéséhez először meg kell értenünk a tulajdonság-alapú tesztelés alapvető ötletét. Hasonlítsuk össze a hagyományos, példa-alapú teszteléssel, amit mindannyian ismerünk.
Példa-alapú tesztelés: Az ismerős út
Képzelje el, hogy írt egy egyedi rendező függvényt, a my_sort()
-ot. Példa-alapú teszteléssel a gondolkodási folyamata a következő lenne:
- "Teszteljük egy egyszerű, rendezett listával." ->
assert my_sort([1, 2, 3]) == [1, 2, 3]
- "Mi van egy fordított sorrendű listával?" ->
assert my_sort([3, 2, 1]) == [1, 2, 3]
- "Mi a helyzet egy üres listával?" ->
assert my_sort([]) == []
- "Egy lista ismétlődésekkel?" ->
assert my_sort([5, 1, 5, 2]) == [1, 2, 5, 5]
- "És egy lista negatív számokkal?" ->
assert my_sort([-1, -5, 0]) == [-5, -1, 0]
Ez hatékony, de van egy alapvető korlátja: csak azokat az eseteket teszteli, amelyekre gondolni tud. A tesztjei csak annyira jók, amennyire a képzelete. Kihagyhat szélsőértékeket, amelyek nagyon nagy számokat, lebegőpontos pontatlanságokat, specifikus Unicode karaktereket vagy összetett adatkombinációkat érintenek, amelyek váratlan viselkedéshez vezetnek.
Tulajdonság-alapú tesztelés: Invariánsokban való gondolkodás
A tulajdonság-alapú tesztelés megfordítja a forgatókönyvet. Ahelyett, hogy specifikus példákat adna meg, Ön definiálja a tulajdonságait, vagy invariánsait, a függvényének – olyan szabályokat, amelyeknek bármely érvényes bemenetre igaznak kell lenniük. A my_sort()
függvényünk esetében ezek a tulajdonságok a következők lehetnek:
- A kimenet rendezett: Bármely számlista esetén a kimeneti lista minden eleme kisebb vagy egyenlő az őt követő elemmel.
- A kimenet ugyanazokat az elemeket tartalmazza, mint a bemenet: A rendezett lista csak az eredeti lista permutációja; nincsenek hozzáadott vagy elveszett elemek.
- A függvény idempotens: Egy már rendezett lista rendezése nem változtathatja meg azt. Azaz,
my_sort(my_sort(some_list)) == my_sort(some_list)
.
Ezzel a megközelítéssel nem a tesztadatokat írja meg. Ön a szabályokat írja meg. Ezután hagyja, hogy egy keretrendszer, például a Hypothesis, több száz vagy ezer véletlenszerű, változatos és gyakran ravasz bemenetet generáljon, hogy megpróbálja bebizonyítani a tulajdonságai téves voltát. Ha talál egy olyan bemenetet, amely megsért egy tulajdonságot, akkor hibát talált.
Bemutatkozik a Hypothesis: Az Ön automatizált tesztadat-generátora
A Hypothesis a vezető tulajdonság-alapú tesztelő könyvtár Pythonhoz. Fogja az Ön által definiált tulajdonságokat, és elvégzi azt a kemény munkát, hogy tesztadatokat generáljon azok kihívására. Ez nem csak egy véletlenszerű adatgenerátor; ez egy intelligens és hatékony eszköz, amelyet a hibák hatékony megtalálására terveztek.
A Hypothesis főbb jellemzői
- Automatikus teszteset-generálás: Ön határozza meg a szükséges adatok *formáját* (pl. "egész számok listája", "csak betűket tartalmazó sztring", "jövőbeli dátum és idő"), és a Hypothesis széles skálájú példákat generál, amelyek megfelelnek ennek a formának.
- Intelligens zsugorítás (Shrinking): Ez a varázslatos funkció. Amikor a Hypothesis hibás tesztesetet talál (pl. egy 50 komplex számból álló lista, amely összeomlasztja a rendező függvényt), nem csak azt a hatalmas listát jelenti. Intelligensen és automatikusan egyszerűsíti a bemenetet, hogy megtalálja a legkisebb lehetséges példát, amely még mindig okozza a hibát. Egy 50 elemből álló lista helyett azt jelentheti, hogy a hiba mindössze
[inf, nan]
értékkel fordul elő. Ez hihetetlenül gyorssá és hatékonnyá teszi a hibakeresést. - Zökkenőmentes integráció: A Hypothesis tökéletesen integrálódik olyan népszerű tesztelő keretrendszerekkel, mint a
pytest
és azunittest
. Tulajdonság-alapú teszteket adhat hozzá meglévő példa-alapú tesztjei mellé anélkül, hogy megváltoztatná a munkafolyamatát. - Stratégiák gazdag könyvtára: Beépített "stratégiák" hatalmas gyűjteményével rendelkezik, amelyek mindent generálnak, az egyszerű egész számoktól és sztringektől kezdve a komplex, beágyazott adatstruktúrákig, időzóna-tudatos dátum- és időpontokig, sőt még NumPy tömbökig is.
- Állapot-alapú tesztelés (Stateful Testing): Összetettebb rendszerek esetén a Hypothesis képes műveletek sorozatát tesztelni, hogy hibákat találjon az állapotátmenetekben, ami példa-alapú teszteléssel köztudottan nehéz.
Kezdő lépések: Az első Hypothesis tesztje
Lássunk hozzá a gyakorlati részhez. A Hypothesis megértésének legjobb módja, ha működés közben látjuk.
Telepítés
Először telepítenie kell a Hypothesist és az Ön által választott tesztfuttatót (mi a pytest
-et fogjuk használni). Ez olyan egyszerű, mint:
pip install pytest hypothesis
Egy egyszerű példa: Egy abszolútérték-függvény
Nézzünk egy egyszerű függvényt, amelynek egy szám abszolút értékét kellene kiszámítania. Egy kissé hibás implementáció így nézhet ki:
# a `my_math.py` nevű fájlban def custom_abs(x): """Az abszolút érték függvény egyedi implementációja.""" if x < 0: return -x return x
Most írjunk egy tesztfájlt, a test_my_math.py
-t. Először a hagyományos pytest
megközelítés:
# test_my_math.py (Példa-alapú) def test_abs_positive(): assert custom_abs(5) == 5 def test_abs_negative(): assert custom_abs(-5) == 5 def test_abs_zero(): assert custom_abs(0) == 0
Ezek a tesztek átmennek. A függvényünk helyesnek tűnik ezen példák alapján. De most írjunk egy tulajdonság-alapú tesztet a Hypothesis segítségével. Mi az abszolút érték függvény alapvető tulajdonsága? Az eredmény soha nem lehet negatív.
# test_my_math.py (Tulajdonság-alapú Hypothesis-szel) from hypothesis import given from hypothesis import strategies as st from my_math import custom_abs @given(st.integers()) def test_abs_property_is_non_negative(x): """Tulajdonság: Bármely egész szám abszolút értéke mindig >= 0.""" assert custom_abs(x) >= 0
Nézzük meg részletesebben:
from hypothesis import given, strategies as st
: Importáljuk a szükséges komponenseket. Agiven
egy dekorátor, amely egy hagyományos tesztfüggvényt tulajdonság-alapú tesztté alakít. Astrategies
az a modul, ahol az adatgenerátorainkat találjuk.@given(st.integers())
: Ez a teszt magja. A@given
dekorátor azt mondja a Hypothesisnek, hogy többször futtassa le ezt a tesztfüggvényt. Minden futtatásnál generál egy értéket a megadott stratégia, azst.integers()
segítségével, és átadja aztx
argumentumként a tesztfüggvényünknek.assert custom_abs(x) >= 0
: Ez a tulajdonságunk. Azt állítjuk, hogy bármelyx
egész számra, amit a Hypothesis kitalál, a függvényünk eredményének nagyobbnak vagy egyenlőnek kell lennie nullával.
Amikor ezt futtatja a pytest
-tel, valószínűleg sok értékre át fog menni. A Hypothesis megpróbálja a 0-t, -1-et, 1-et, nagy pozitív számokat, nagy negatív számokat és még sok mást. Egyszerű függvényünk mindezeket helyesen kezeli. Most próbáljunk ki egy másik stratégiát, hogy lássuk, találunk-e gyengeséget.
# Teszteljük lebegőpontos számokkal @given(st.floats()) def test_abs_floats_property(x): assert custom_abs(x) >= 0
Ha ezt futtatja, a Hypothesis gyorsan talál egy hibás esetet!
Falsifying example: test_abs_floats_property(x=nan) ... assert custom_abs(nan) >= 0 AssertionError: assert nan >= 0
A Hypothesis felfedezte, hogy függvényünk, amikor float('nan')
(Not a Number) értéket kap, nan
-t ad vissza. A nan >= 0
állítás hamis. Épp most találtunk egy finom hibát, amelyre valószínűleg nem gondoltunk volna manuális tesztelés során. Javíthatnánk a függvényünket, hogy kezelje ezt az esetet, például egy ValueError
kiváltásával vagy egy specifikus érték visszaadásával.
Még jobb, mi van, ha a hiba egy nagyon specifikus lebegőpontos számmal volt? A Hypothesis zsugorítója egy nagy, komplex, hibás számot a legegyszerűbb lehetséges verzióra redukált volna, ami még mindig kiváltja a hibát.
A stratégiák ereje: Tesztadatok készítése
A stratégiák a Hypothesis szíve. Ezek adatok generálására szolgáló receptek. A könyvtár beépített stratégiák széles skáláját tartalmazza, és ezeket kombinálva és testreszabva gyakorlatilag bármilyen elképzelhető adatstruktúrát generálhat.
Gyakori beépített stratégiák
- Numerikus:
st.integers(min_value=0, max_value=1000)
: Egész számokat generál, opcionálisan meghatározott tartományon belül.st.floats(min_value=0.0, max_value=1.0, allow_nan=False, allow_infinity=False)
: Lebegőpontos számokat generál, speciális értékek finomhangolt szabályozásával.st.fractions()
,st.decimals()
- Szöveg:
st.text(min_size=1, max_size=50)
: Meghatározott hosszúságú Unicode sztringeket generál.st.text(alphabet='abcdef0123456789')
: Sztringeket generál egy adott karakterkészletből (pl. hexadecimális kódokhoz).st.characters()
: Egyedi karaktereket generál.
- Gyűjtemények:
st.lists(st.integers(), min_size=1)
: Listákat generál, ahol minden elem egész szám. Figyelje meg, hogyan adunk át egy másik stratégiát argumentumként! Ezt kompozíciónak nevezzük.st.tuples(st.text(), st.booleans())
: Rögzített struktúrájú tuple-ket generál.st.sets(st.integers())
st.dictionaries(keys=st.text(), values=st.integers())
: Szótárakat generál meghatározott kulcs- és értéktípusokkal.
- Időbeli:
st.dates()
,st.times()
,st.datetimes()
,st.timedeltas()
. Ezek időzóna-tudatossá tehetők.
- Vegyes:
st.booleans()
:True
vagyFalse
értéket generál.st.just('constant_value')
: Mindig ugyanazt az egyetlen értéket generálja. Hasznos komplex stratégiák összeállításához.st.one_of(st.integers(), st.text())
: Értéket generál a megadott stratégiák egyikéből.st.none()
: CsakNone
értéket generál.
Stratégiák kombinálása és átalakítása
A Hypothesis igazi ereje abban rejlik, hogy képes komplex stratégiákat építeni egyszerűbbekből.
A .map()
használata
A .map()
metódus lehetővé teszi, hogy egy stratégiából származó értéket átalakítson valami mássá. Ez tökéletes az egyedi osztályainak objektumainak létrehozásához.
# Egy egyszerű adat osztály from dataclasses import dataclass @dataclass class User: user_id: int username: str # Egy stratégia User objektumok generálására user_strategy = st.builds( User, user_id=st.integers(min_value=1), username=st.text(min_size=3, alphabet='abcdefghijklmnopqrstuvwxyz') ) @given(user=user_strategy) def test_user_creation(user): assert isinstance(user, User) assert user.user_id > 0 assert user.username.isalpha()
A .filter()
és az assume()
használata
Néha el kell utasítania bizonyos generált értékeket. Például szükség lehet egy egész számok listájára, ahol az összeg nem nulla. Használhatja a .filter()
-t:
st.lists(st.integers()).filter(lambda x: sum(x) != 0)
Azonban a .filter()
használata hatékonytalan lehet. Ha a feltétel gyakran hamis, a Hypothesis sok időt tölthet egy érvényes példa generálásával. Gyakran jobb megközelítés az assume()
használata a tesztfüggvényen belül:
from hypothesis import assume @given(st.lists(st.integers())) def test_something_with_non_zero_sum_list(numbers): assume(sum(numbers) != 0) # ... az Ön tesztlogikája itt ...
Az assume()
azt mondja a Hypothesisnek: "Ha ez a feltétel nem teljesül, egyszerűen dobja el ezt a példát, és próbáljon ki egy újat." Ez egy közvetlenebb és gyakran hatékonyabb módja a tesztadatok korlátozásának.
Az st.composite()
használata
Valóban komplex adatgenerálás esetén, ahol az egyik generált érték a másiktól függ, az st.composite()
a szükséges eszköz. Lehetővé teszi, hogy írjon egy függvényt, amely speciális draw
függvényt vesz argumentumként, amellyel lépésről lépésre húzhat értékeket más stratégiákból.
Klasszikus példa egy lista és egy érvényes index generálása a listához.
@st.composite def list_and_index(draw): # Először húzzon egy nem üres listát my_list = draw(st.lists(st.integers(), min_size=1)) # Aztán húzzon egy indexet, amely garantáltan érvényes az adott listára index = draw(st.integers(min_value=0, max_value=len(my_list) - 1)) return (my_list, index) @given(data=list_and_index()) def test_list_access(data): my_list, index = data # Ez a hozzáférés garantáltan biztonságos, mivel így építettük fel a stratégiát element = my_list[index] assert element is not None # Egy egyszerű állítás
A Hypothesis működés közben: Valós forgatókönyvek
Alkalmazzuk ezeket a fogalmakat valósághűbb problémákra, amelyekkel a szoftverfejlesztők nap mint nap szembesülnek.
1. forgatókönyv: Adatszerializációs függvény tesztelése
Képzeljen el egy függvényt, amely szerializál egy felhasználói profilt (egy szótárat) egy URL-biztonságos sztringgé, és egy másikat, amely deszerializálja azt. Kulcsfontosságú tulajdonság, hogy a folyamatnak tökéletesen visszafordíthatónak kell lennie.
import json import base64 def serialize_profile(data: dict) -> str: """Szerializál egy szótárat URL-biztonságos base64 sztringgé.""" json_string = json.dumps(data) return base64.urlsafe_b64encode(json_string.encode('utf-8')).decode('utf-8') def deserialize_profile(encoded_str: str) -> dict: """Deszerializál egy sztringet vissza szótárrá.""" json_string = base64.urlsafe_b64decode(encoded_str.encode('utf-8')).decode('utf-8') return json.loads(json_string) # Most a teszthez # Szükségünk van egy stratégiára, amely JSON-kompatibilis szótárakat generál json_dictionaries = st.dictionaries( keys=st.text(), values=st.recursive(st.none() | st.booleans() | st.floats(allow_nan=False) | st.text(), lambda children: st.lists(children) | st.dictionaries(st.text(), children), max_leaves=10) ) @given(profile=json_dictionaries) def test_serialization_roundtrip(profile): """Tulajdonság: Egy kódolt profil deszerializálása az eredeti profilt kell, hogy visszaadja.""" encoded = serialize_profile(profile) decoded = deserialize_profile(encoded) assert profile == decoded
2. forgatókönyv: Rendezési algoritmus tesztelése
Térjünk vissza a rendezési példánkhoz. Így tesztelné a korábban definiált tulajdonságokat.
from collections import Counter def my_buggy_sort(numbers): # Vezessünk be egy finom hibát: eldobja az ismétlődéseket return sorted(list(set(numbers))) @given(st.lists(st.integers())) def test_sorting_properties(numbers): sorted_list = my_buggy_sort(numbers) # 1. Tulajdonság: A kimenet rendezett for i in range(len(sorted_list) - 1): assert sorted_list[i] <= sorted_list[i+1] # 2. Tulajdonság: Az elemek megegyeznek (ez találja meg a hibát) assert Counter(numbers) == Counter(sorted_list) # 3. Tulajdonság: A függvény idempotens assert my_buggy_sort(sorted_list) == sorted_list
Amikor lefuttatja ezt a tesztet, a Hypothesis gyorsan talál egy hibás példát a 2. tulajdonságra, például numbers=[0, 0]
. Függvényünk [0]
-t ad vissza, és Counter([0, 0])
nem egyenlő Counter([0])
-vel. A zsugorító gondoskodik arról, hogy a hibás példa a lehető legegyszerűbb legyen, azonnal nyilvánvalóvá téve a hiba okát.
3. forgatókönyv: Állapot-alapú tesztelés
Az idővel változó belső állapotú objektumok (például adatbázis-kapcsolat, bevásárlókosár vagy gyorsítótár) esetében hihetetlenül nehéz hibákat találni. Egy hiba kiváltásához egy specifikus műveletsorra lehet szükség. A Hypothesis pontosan erre a célra biztosítja a `RuleBasedStateMachine`-t.
Képzeljen el egy egyszerű API-t egy memórián belüli kulcs-érték tárolóhoz:
class SimpleKeyValueStore: def __init__(self): self._data = {} def set(self, key, value): self._data[key] = value def get(self, key): return self._data.get(key) def delete(self, key): if key in self._data: del self._data[key] def size(self): return len(self._data)
Modellezhetjük a viselkedését, és tesztelhetjük egy állapotgéppel:
from hypothesis.stateful import RuleBasedStateMachine, rule, Bundle class KeyValueStoreMachine(RuleBasedStateMachine): def __init__(self): super().__init__() self.model = {} self.sut = SimpleKeyValueStore() # A Bundle() a szabályok közötti adatok átadására szolgál keys = Bundle('keys') @rule(target=keys, key=st.text(), value=st.integers()) def set_key(self, key, value): self.model[key] = value self.sut.set(key, value) return key @rule(key=keys) def delete_key(self, key): del self.model[key] self.sut.delete(key) @rule(key=st.text()) def get_key(self): model_val = self.model.get(key) sut_val = self.sut.get(key) assert model_val == sut_val @rule() def check_size(self): assert len(self.model) == self.sut.size() # A teszt futtatásához egyszerűen örökölje az osztályt a gépből és a unittest.TestCase-ből # Pytest-ben egyszerűen hozzárendelheti a tesztet a gép osztályához TestKeyValueStore = KeyValueStoreMachine.TestCase
A Hypothesis most véletlenszerű `set_key`, `delete_key`, `get_key` és `check_size` műveletsorokat hajt végre, könyörtelenül próbálva olyan sorozatot találni, amely az egyik állítás hibájához vezet. Ellenőrzi, hogy egy törölt kulcs lekérése helyesen működik-e, hogy a méret konzisztens-e több beállítás és törlés után, és sok más forgatókönyvet, amelyekre manuálisan nem gondolna.
Bevált gyakorlatok és haladó tippek
- A példa adatbázis: A Hypothesis okos. Amikor hibát talál, elmenti a hibás példát egy helyi könyvtárba (
.hypothesis/
). A következő alkalommal, amikor futtatja a tesztjeit, először ezt a hibás példát fogja újra lejátszani, azonnali visszajelzést adva arról, hogy a hiba még mindig fennáll. Amint kijavítja, a példa már nem kerül újra lejátszásra. - A tesztvégrehajtás szabályozása az
@settings
segítségével: A tesztfutás számos aspektusát szabályozhatja az@settings
dekorátorral. Növelheti a példák számát, határidőt állíthat be egyetlen példa futtatására (végtelen ciklusok elkapására), és kikapcsolhat bizonyos állapotellenőrzéseket.@settings(max_examples=500, deadline=1000) # Futtass 500 példát, 1 másodperces határidő @given(...) ...
- Hibák reprodukálása: Minden Hypothesis futtatás kiír egy seed értéket (pl.
@reproduce_failure('version', 'seed')
). Ha egy CI szerver olyan hibát talál, amelyet Ön helyileg nem tud reprodukálni, használhatja ezt a dekorátort a megadott seed-del, hogy a Hypothesist ugyanazt a példasorozatot futtassa le. - Integráció CI/CD-vel: A Hypothesis tökéletesen illeszkedik bármely folyamatos integrációs pipeline-ba. Képessége, hogy ismeretlen hibákat találjon, mielőtt azok elérik a gyártást, felbecsülhetetlen értékű biztonsági hálóvá teszi.
A gondolkodásmódváltás: Tulajdonságokban való gondolkodás
A Hypothesis alkalmazása több, mint egy új könyvtár megtanulása; a kód helyességéről való gondolkodás új módjának elsajátításáról van szó. Ahelyett, hogy azt kérdezné: "Milyen bemeneteket teszteljek?", azt kezdi kérdezni: "Melyek a kódra vonatkozó egyetemes igazságok?"
Íme néhány kérdés, amelyek segítenek a tulajdonságok azonosításában:
- Létezik-e fordított művelet? (pl. szerializálás/deszerializálás, titkosítás/visszafejtés, tömörítés/kibontás). A tulajdonság az, hogy a művelet és annak fordítottja végrehajtása az eredeti bemenetet kell, hogy eredményezze.
- Idempotens-e a művelet? (pl.
abs(abs(x)) == abs(x)
). A függvény többszöri alkalmazása ugyanazt az eredményt kell, hogy adja, mint az egyszeri alkalmazás. - Létezik-e egy másik, egyszerűbb módja ugyanaz eredmény kiszámításának? Tesztelheti, hogy a komplex, optimalizált függvénye ugyanazt a kimenetet produkálja-e, mint egy egyszerű, nyilvánvalóan helyes verzió (pl. a saját elegáns rendezőjének tesztelése a Python beépített
sorted()
függvényével szemben). - Mi kell, hogy mindig igaz legyen a kimenetre vonatkozóan? (pl. egy `find_prime_factors` függvény kimenetének csak prím számokat kell tartalmaznia, és szorzatuknak meg kell egyeznie a bemenettel).
- Hogyan változik az állapot? (Állapot-alapú teszteléshez) Milyen invariánsokat kell fenntartani bármely érvényes művelet után? (pl. egy bevásárlókosárban lévő tételek száma soha nem lehet negatív).
Összefoglalás: Új szintű bizalom
A tulajdonság-alapú tesztelés a Hypothesis segítségével nem helyettesíti a példa-alapú tesztelést. Továbbra is szüksége van specifikus, kézzel írott tesztekre a kritikus üzleti logika és a jól ismert követelmények (pl. "Egy X országból származó felhasználónak Y árat kell látnia") esetében.
A Hypothesis egy hatékony, automatizált módszert biztosít a kód viselkedésének feltárására és az előre nem látott szélsőértékek elleni védelemre. Fáradhatatlan partnerként működik, több ezer olyan tesztet generálva, amelyek változatosabbak és ravaszabbak, mint amennyit bármely ember reálisan megírhatna. A kód alapvető tulajdonságainak meghatározásával egy robusztus specifikációt hoz létre, amelyet a Hypothesis tesztelhet, így új szintű bizalmat kap a szoftverében.
Amikor legközelebb ír egy függvényt, szánjon egy pillanatot arra, hogy túlmutasson a példákon. Kérdezze meg magától: "Mik a szabályok? Mi kell, hogy mindig igaz legyen?" Aztán hagyja, hogy a Hypothesis végezze el a kemény munkát, megpróbálva feltörni azokat. Meg fog lepődni, mit talál, és a kódja jobb lesz tőle.